Module II : Using advanced ansible solution
1 - Using task control
- Using loops and items
- Loops allows you to iterate through a simple list of items
- Each item in a loop can be a dictionary with multiple keys
- Using Register variables with loops
- Registers are used to store the output of a command and address it as a variable
- You can use the register then in a conditional or in a loop
- Using when to run tasks conditionally
- when are used to run tasks conditionally
- A condition can be used to run a task only if the condition is true.
- Playbooks vars, regs vars, and facts can be used in conditions and make sure that tasks only run if specific conditions are true
- For instance, check if a user/file exists, if a certain amount of memory is available, etc.
- The simplest example of a condition, is to check whether a Boolean variable is true or false.
- You can also check and see if a non-boolean variable has a value and use that value in the conditional. Or use a conditional in which you compare the value of a fact to the specific value of a variable
- Examples :
- ansible_machine == “x86_64”
- ansible_distribution_major_version == “8”
- ansible_memfree_mb (==,<,>,≤,≠) 1024
- my_var is defined
- my_var is not defined
- my_var (equal my_var is defined).
- ansible_distribution in supported_distos
- Examples :
- Testing Multiple Conditions
- Use and or or and group the conditions with ()
- example :
- when: ansible_distribution == “CentOs” or ansible_distribution == “RedHat”
- when: ansible_machine == “x86_64” and ansible_distribution == “CentOS”
- example :
- You can also test the condition using a list and then all of the condition must be true
- Use and or or and group the conditions with ()
- Using handlers
- Handlers allow you to configure playbooks in a way that one task will only run if another task has been running successfully
- Use the notify statement from the main task to trigger the handler
- Handlers are typically used to restart services or reboot hosts
- use force_handlers: True to run handlers in case of failure.
- Using blocks
- a block is a logical group of tasks and they are used to control how tasks are executed.
- one block can be enabled using a single when
- blocks can be used in error condition handling
- Use block to define the main tasks to run
- use rescue to define tasks that will run in case the tasks in the block fail.
- Use always to define tasks that will run, regardless the success or failure of the block and rescue tasks
- Dealing with failures
- Ansible looks at the exit status of a task to determine whether it has failed.
- When any task fails, Ansible aborts the rest of the play on that host and continues with the next host
- Use ignore_errors in a task to ignore failures
- Use force_handlers to force a handler that has been triggered to run ,even if another task fails.
- Use failed_when to identify when the task has failed.
- fail module can be used to print a message that informs why a task has failed. ignore_errors must set to yes
- Make sure to register the result of the command and analyze the output whenever using failed_when or fail.
- Managing changed status
- Managing the changed status may be important, as handlers trigger the changed status
- The result of a command can be registered, and the registered variable can be scanned for specific text to determine that a change has occurred
- This allows ansible to report a changed status, where it normally would not, thus allowing handlers to be triggered
- using change_when is usable in two cases:
- To allow handlers to run when a change would not normally trigger.
- To disable commands that run successful to report a changed status
- Using loops and items
2 - Deploying files using JINJA2 templates
- Using Modules to Manipulate Files:
- Modules are availabe for managing files:
- lineinfile : ensures that a line is in a file, useful for changing a single line in a file.
- blockinfile: manipulates multi-line blocks of text in files.
- copy: copies a file from local or remote machine to a location on a managed host.
- fetch: used to fetch a file from a remote machine and store it on the controller node.
- file: sets attributes to file, and can also create and remove files, symbolic links and more.
- Modules are availabe for managing files:
- Managing SELinux File Context
- file module.
- sefcontext : manages SELinux file context in the SELinux Policy (but not on files)
⇒After using the secontext you will need to run the command/file module to applicate the changes “restrocon”
- the file module sets SELinux context directly on the file (like the chcon command) and not in the policy.
- Using Jinja2 Templates
- While lineinfile and blockinfile can be used t apply simple modifications to files. Jinja2 templates can be used for more advanced modifications
- While using templates, the target files are automatically customized using vars and facts.
- in a jinja2 template, you will find multiple elements :
- data
- variables
- expressions
- control structures
- The vars in the template are replaced with their values when the jinja2 template is rendered to the target file on the managed host.
- If using variables, they can be specified using the vars section of the playbook.
- Alternatively, Ansible facts can be used as variables.
- To prevent administrators from overwriting files that are managed by Ansible, set the
ansible_managed = Ansible managed
in ansible.cfg, also on top of the jinja2 template, set the {{ ansible_managed }} variable.
- Using Control Structures in Jinja2
- In jinja2 templates, control structures can be used to organize the template in an optimal way.
- The for statement can be used to iterate through a variable and use all values in the variable
- The if statement can be used to have the template work with a variable if another variable is defined.
- Using Modules to Manipulate Files:
3 - Using Ansible Roles
- Understanding Directory Structure Best Practices
- When working with Ansible, it’s recommended to use project directories so that contents can be organized in a consistent way.
- Each project directory may have its own ansible.cfg, inventory as well as playbooks.
- Roles can be used to standardize and easily re-use specific parts of Ansible
- Role is a complete project dedicated to a specific task that is going to be included in the main playbook
- Understanding Ansible Roles
- Code used in one playbook can be useful in other playbooks also.
- A role is a collections of tasks, variables, files, templates and other resources in a fixed directory structure that, as such, can be easily included from a playbook
- Typical directories inside a role:
- defaults : contains default values of role vars.
- files : contains static files that are needed from the role tasks
- handlers : has a main.yml that defines handlers used in the role.
- meta : has a main.yml that may be used to include role metadata, such as info about the author, license, dependencies, and more.
- tasks: contains a main.yml that defines the role task definitions
- templates: used to store jinja2 templates
- tests: may contain an optimal inventory file, as well as test.yml playbook that can be used to test the role.
- vars: may contain a main.yml with standard variables for the role (not meant to be overwritten)
- Variables can be defined at different levels in a role :
- vars/main.yml ⇒ has the role default vars, and they are not intended to be overwritten
- defaults/main.yml ⇒ contains default vars, but they have a low precedence and can be overwritten.
- Understanding role location :
- Role can be stored at a default location, and from there can easily be used from playbooks.
- ./roles has highest precedence
- ~/.ansible/roles is checked after that
- /etc/ansible/roles is checked next
- /usr/share/ansible/roles is check last.
- Role can be stored at a default location, and from there can easily be used from playbooks.
- Using Roles in a playbook :
- When roles are used, they will run before any task that is defined in the playbook.
- Defining Role variables :
- When calling a role, role variables can be defined
- role: role1
- role: role2 var1: man var2: women
- When calling a role, role variables can be defined
- Using Ansible Galaxy for standard Roles
- Admins can define their own roles, or standard roles can be used from Ansible-Galaxy
- Ansible-Galaxy is a public website where community provided roles are offered
- An easy-to-use search interface is available at galaxy.ansible.com
- Using the Ansible Galaxy CLI
- ansible-galaxy search will search for roles
- If an argument is provided, ansible-galaxy will search for this arg in the role description
- Use —author, —platforms and galaxy-tags to narrow down the search results
=> ansible-galaxy search wordpress —platforms El
ansible-galaxy info bertvv.wordpress
⇒ provides infos about roles- ansible-galaxy install downloads a role and installs it in ~/.ansible/roles
- ansible-galaxy list shows installed roles
- ansible-galaxy remove can be used to clean up and remove roles
- ansible-galaxy init creates a directory structure that can be used to start developing your own role exp:
ansible-galaxy init <user_name>.<role_name>
- Using a requirements File
- ansible-galaxy can be used to install a list of roles based on definition in a requirements file
- A requirement file is a YAML file that defines a list of required roles that are specified using the src keyword
- src keyword can contain the name of a role from Ansible Galaxy, or a URL to a custom location pointing to your own roles
- Create roles/requirements.yml in the projects directory to use it, and always specify the optional version attribute, to avoid getting surprises when a newer version of a role has become available.
- - src: file:///opt/local/roles/myrole.tar name: myrole version: 1.0
- to install roles using requirements file, use :
ansible-galaxy install -r roles/requirement.yml
- ansible-galaxy search will search for roles
- Creating Custom Roles
ansible-galaxy init <role_name>
to create the role directory structure
- Mind the location of the directory structure, you can put it in the local project directory or in a directory that is accessible for all projects
- Populate the required role files as discussed before
- Best practices :
- Each role should have its own version control repository
- Don’t put sensitive information in the role, but in the local playbooks or Ansible Vault instead
- don’t forget to edit README.md and meta/main.yml
- Roles should be dedicated to one task/function. Use multiple roles to manage multiple tasks/functions
- role dependencies can be defined in meta/main.yml
- Dependencies listed here will be installed automatically when role is used.
- Conditional roles call a role dynamically, using the include_role module
- Conditional roles can be comined with conditional statements
- This makes it so a role will only run if the conditional statement is true
- use include_role in a task statement to do so..
- ⇒ example :
- - - - hosts : <hostname> tasks: - include_role: name: wrole_name> when : <condition>
- ⇒ example :
- Managing order of Execution
- Role tasks are always executed before playbook tasks
- Next, playbook tasks are executed and then, handlers are executed
- Use pre_tasks to define playbook tasks are to be executed before the tasks in a role.
- Use post_tasks to define playbook tasks that are executed after playbook tasks and roles.
- Understanding Collections:
- Collection are a new way to bundle Ansible contents to make it more manageable
- Collections can have different sources, including the Ansible community and RedHat partners.
- The collection may contain the following:
- modules: that provide the ansible core functionality
- roles: that can be included in playbooks for common task execution.
- plugins: that extend the python code on the Ansible control host.
- Collections have a Fully Qualified Collection Name (FQCN), such as ansible.netcommon
- Before collection, you would address just a module name such as user, now you address this module as ansible.builtin.user
- collections keyword is used to define the collection you are going to use in the play header.
exp ⇒
ansible.posix.selinux <> selinux
- Example of collections/requirements.yml - collection: - name: <collec_name> - source
- collections name can be found easly using ansible-doc -l | grep aws
ansible-galaxy collection list
to list all the collection
- Investigate in : ~/.ansible/collections/ansible_collections/ansible/posix
- example : ansible-doc ansible.posix.selinux
- Understanding Directory Structure Best Practices
4 - Using RHEL system Roles
- Understanding RHEL system Roles
- RHEL system roles are provided to configure standard RHEL operations
- rhel-system-roles.kdump configures the kdump crash recovery service
- rhel-system-roles.network configures network interfaces
- rhel-system-roles.selinux manages all aspects of selinux.
- rhel-system-roles.timesync is used to setup Network time protocol or precision time protocol
- rhel-system-roles.postfix is used to config a host as a postfix MTA
- rhel-system-roles.firewall config a firewall.
- rhel-system-roles.tuned config the tuned service
- RHEL system roles are provided to configure standard RHEL operations
- Installing RHEL System Roles
- Use
yum install rhel-system-roles
to install them in /usr/share/ansible/roles directory, and a symbolic link is provided with the name Linux-system-roles to provide maximum compatibility.
- Use
- Using the RHEL SELinux System Role
- The rhel-system-role.selinux can do several things:
- set enforcing or permissive mode / run restorecon / set selinux file context / set seliux user mappings.
- a reboot is required after making changes
- Variables:
- selinux_state
- selinux_booleans
- selinux_fcontexts
- selinux_restore_dirs
- selinux_ports
- The rhel-system-role.selinux can do several things:
- Using the RHEL TimeSync System Role.
- rhel-system-roles.timesync can be used to manage time synchronization
- vars:
- timesync_ntp_servers
- hostname: shows the hostname
- iburst: specifies that fast iburst sync should be used.
- timezone
- timesync_ntp_servers
- Understanding RHEL system Roles
5 - Using ansible in large environments
- Managing inventory
- Multiple inventories can be used, for instance by putting multiple inventory files in a directory and use that as the source of inventory.
- If the inventory specified is a dir, all inventory files in that dir are considered includ dynamic and static invs.
- Addressing Host Patterns
- “&” ⇒ commun hosts ”!” ⇒ but not ”*” ⇒ wildcard
- Configuring Parallelism
- Ansible can run on multiple managed hosts simultaneously, but by default the maximum number of simultaneous hosts is limited to five
- set forks =n in ansible.cfg to change the maximum number of simultaneous hosts or use -f nn as args to ansible[-playbook] command.
- user serial keyword to run hosts through the entire play in batches
- Organizing Directory Structure
- if playbooks grow larger, it is common to use modularity by using includes and imports
- use import_playbook to import a playbook.
- task file is a flat list of tasks, use import_tasks to statically import a task file in the playbook, it will be included at the location where it is imported.
- use include_tasks to include a task file
- Managing inventory
6 - Ansible troubleshooting
- Managing Ansible logs
- By default, ansible is not configured to log its output anywhere
- set log_path in ansible.cfg to write logs to a specific destination
- ⇒ create this file under the project file
- make sure to use log rotation.
- Using the debug module
- debug module is used to show values of vars in a playbook
- Also to show msgs in specific errors situations
- Using check mode
- use
ansible-playbook --check / --diff for changes made by the templates files on the nodes)
- modules in the playbook must support check mode
- set check_mode: yes within the task to always run that specific task in check mode
- use
- Using Modules for Troubleshooting and testing
- uri: checks content that is returned from a specific URL
- script: runs a script from the control node on the managed hosts
- stat: check the status of fils; use it to register a variable and next test to determine if a file exist.
- atime: last access time of the file
- isdir: true if file is a dir
- exists: true if file exists
- size: size in bytes
- assert: this module will fail with an error if a specific condition is not met.
- Troubleshooting connection issues
- Authentication issues:
- Confirm remote_user setting and existence.
- confirm host key setup
- verify become and become_user
- check sudo
- Connecting to managed hosts:
- Using adhoc commands!
- ping to test network connectivity
--become
to test privescansible node1 -m ping --become
- command module to test diff items.
- Authentication issues:
- Analyzing playbook errors
- verbosity levels:
- -v : output data
- -vv : output and input data
- -vvv : +connections info
- -vvvv : additional info, executed scripts and the user who’s running tasks.
- avoiding errors in playbook best practices
- name of plays should make sense
- include comments
- use includes and imports when the play gets bigger
- Managing Ansible logs